package com.xiaoyu.base.data;

import android.content.Context;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.tencent.bugly.crashreport.CrashReport;
import com.xiaoyu.base.BaseConstants;
import com.xiaoyu.base.app.AppContext;
import com.xiaoyu.base.AppDatabase;
import com.xiaoyu.base.GlobalDatabase;

import in.srain.cube.app.init.InitializableAsync;
import in.srain.cube.app.init.InitializableMultiProcessSync;
import in.srain.cube.app.init.InitializableSync;
import in.srain.cube.util.UTUtil;

import in.srain.cube.util.log.LogClientManagerProxy;
import com.xiaoyu.base.model.User;
import com.xiaoyu.base.utils.SharedPreferencesData;
import com.xiaoyu.base.model.auth.TokenInfo;

import in.srain.cube.request.JsonData;
import in.srain.cube.util.CLog;
import in.srain.cube.util.log.LogEvent;

import static com.xiaoyu.base.log.BaseLogClient.KEY_UID;

public class UserData implements InitializableSync, InitializableAsync, InitializableMultiProcessSync {

    private static final String TAG = "UserData";
    private static final String EMPTY_UID = "";

    private static UserData sInstance = new UserData();

    private final TokenInfo mTokenInfo;
    private final UserKVConfigData mUserKVConfigData;
    private final ExtUserStatusData mExtUserStatus;
    private String mUid;
    private User mUser;

    private UserDataLifecycleListener lifecycleListener;

    private UserData() {
        mTokenInfo = new TokenInfo();
        mUserKVConfigData = new UserKVConfigData();
        mExtUserStatus = ExtUserStatusData.getInstance();
    }

    public static UserData getInstance() {
        return sInstance;
    }

    public void init(UserDataLifecycleListener lifecycleListener) {
        this.lifecycleListener = lifecycleListener;
    }

    public synchronized void tryUpdateSelf(User user) {
        boolean modified = mUser.tryToUpdateFrom(user);
        if (modified) {
            saveUser();
        }
    }

    public synchronized void tryUpdateSelf(JsonData jsonData) {
        User user = User.fromJson(jsonData);
        tryUpdateSelf(user);
    }

    public void afterLogin(JsonData jsonData) {
        JsonData userJsonData = jsonData.optJson("user");
        JsonData tokenData = jsonData.optJson("token");

        CLog.d(TAG, "afterLogin, userJson: %s, tokenJson: %s", userJsonData, tokenData);
        mUser = User.fromJson(userJsonData);
        mUid = mUser.getUid();

        openDatabase();
        mUserKVConfigData.reload();
        lifecycleListener.afterLogin(jsonData);

        saveUser();
        saveUid();

        updateToken(tokenData);

        UTUtil.getInstance().setUserId(mUid);
        LogEvent.putBaseData(KEY_UID, mUid);
        CrashReport.setUserId(mUid);
    }

    @Override
    public void initiateSyncInMultiProcess() {
        mUid = SharedPreferencesData.basic().getString(BaseConstants.SharedPreferencesKey.KEY_USER_ID, "");
        CLog.i(TAG, "initiateSyncInMultiProcess: %s", mUid);
        if (TextUtils.isEmpty(mUid)) {
            CLog.w(TAG, "uid is not found");
        }

        mUserKVConfigData.reload();

        final JsonData tokenJson = mUserKVConfigData.get(UserKVConfigData.KEY_TOKEN_INFO);
        mTokenInfo.restoreFromJson(tokenJson);
    }

    @Override
    public void initiateSync(Context context) {
        mUid = SharedPreferencesData.basic().getString(BaseConstants.SharedPreferencesKey.KEY_USER_ID, "");
        CLog.i(TAG, "initiateAsync: %s", mUid);
        if (TextUtils.isEmpty(mUid)) {
            CLog.w(TAG, "uid is not found");
        }

        openDatabase();

        mUserKVConfigData.reload();

        final JsonData tokenJson = mUserKVConfigData.get(UserKVConfigData.KEY_TOKEN_INFO);
        mTokenInfo.restoreFromJson(tokenJson);
    }

    @Override
    public void initiateAsync(Context context) {
        if (!mTokenInfo.isValidate()) {
            CLog.w(TAG, "mTokenInfo is not validate");
            return;
        }

        mUser = UserInfoDataProvider.getInstance().ensureAndGetUser(mUid);

        final JsonData userExtraJson = mUserKVConfigData.get(UserKVConfigData.KEY_USER_EXTRA);
        final JsonData extUserStatusJson = mUserKVConfigData.get(UserKVConfigData.KEY_EXT_USER_STATUS);
        mExtUserStatus.updateAndSave(extUserStatusJson);

        lifecycleListener.initiateAsync(context, userExtraJson);

        UTUtil.getInstance().setUserId(mUid);
        LogEvent.putBaseData(KEY_UID, mUid);

        CrashReport.setUserId(mUid);
    }

    private void openDatabase() {
        if (!TextUtils.isEmpty(mUid)) {
            AppDatabase.openDatabase(AppContext.getContext(), mUid);
        }
        GlobalDatabase.openDatabase(AppContext.getContext());
    }

    private void saveUser() {
        UserInfoDataProvider.getInstance().updateUser(mUser);
    }

    private void saveUid() {
        SharedPreferencesData.basic().edit().putString(BaseConstants.SharedPreferencesKey.KEY_USER_ID, mUid).apply();
    }

    public String getUid() {
        return mUid;
    }

    public boolean hasUid() {
        return !TextUtils.isEmpty(mUid);
    }

    public User getUser() {
        return mUser;
    }

    public boolean isSelf(String uid) {
        return !TextUtils.isEmpty(mUid) && TextUtils.equals(mUid, uid);
    }

    public boolean hasLogin() {
        return !TextUtils.isEmpty(mUid) && mTokenInfo != null && mTokenInfo.isValidate();
    }

    public boolean isSystemAccount(@NonNull User user) {
        return isSystemAccountUid(user.getUid());
    }

    public boolean isSystemAccountUid(String uid) {
        if (TextUtils.isEmpty(uid)) {
            return false;
        }
        try {
            long uidLong = Long.parseLong(uid);
            // TODO: need remove, temp uid >= 1000 for test before publish
            return uidLong >= 10000 && uidLong <= 100000;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public void notifyNameUpdate(String uid) {
        User user = UserInfoDataProvider.getInstance().fetchFromMemory(uid);
        if (!user.isNobody()) {
            user.notifyNameUpdate();
        }
    }

    public void updateAvatar(String url) {
        mUser.setAvatar(url);
        saveUser();
    }

    public void saveUserKV(String key, JsonData jsonData) {
        if (TextUtils.isEmpty(mUid)) {
            CLog.e(TAG, "saveUserKV, but user has not login: %s %s", key, jsonData);
            return;
        }
        mUserKVConfigData.put(key, jsonData);
    }

    public JsonData getUserKV(String key) {
        return mUserKVConfigData.get(key);
    }

    public TokenInfo getTokenInfo() {
        return mTokenInfo;
    }

    public void updateToken(JsonData tokenJsonData) {
        mTokenInfo.updateFromJson(tokenJsonData);
    }

    public boolean shouldRenewAccessToken() {
        return hasLogin() && mTokenInfo != null && mTokenInfo.shouldRenew();
    }

    public void afterLogout() {
        CLog.d(TAG, "clear");

        mTokenInfo.clear();
        lifecycleListener.afterLogout();
        mUserKVConfigData.clear();
        mUid = EMPTY_UID;
        mUser = User.NOBODY;

        LogEvent.putBaseData(KEY_UID, mUid);
        SharedPreferencesData.basic().edit().putString(BaseConstants.SharedPreferencesKey.KEY_USER_ID, mUid).apply();
    }

    public ExtUserStatusData getExtUserStatus() {
        return mExtUserStatus;
    }

    public void setLifecycleListener(UserDataLifecycleListener lifecycleListener) {
        this.lifecycleListener = lifecycleListener;
    }
}
